<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    /*
        0.yield关键字可以让生成器停止和开始执行
            遇到yield关键字时,执行会停止,通过next()恢复执行
            简单的记法:有几个yield就有几个done:false
     */
    // 0.1.初体验
    function* generatorFn() {
        yield
    }

    let g1 = generatorFn();
    console.log(g1.next())  // {value:undefined,done:false}
    console.log(g1.next())  // {value:undefined,done:true}

    // 0.2.再次体验2
    function* generatorFn2() {
        yield 'a'
        yield 'b'
        return 'c'
    }

    let g2 = generatorFn2();
    console.log(g2.next())  // {value:a,done:false}
    console.log(g2.next())  // {value:b,done:false}
    console.log(g2.next())  // {value:c,done:true}

    // 0.3 会区分作用域,互不影响
    let g3 = generatorFn2();
    let g4 = generatorFn2();
    console.log(g3.next())  // {value:a,done:false}
    console.log(g4.next())  // {value:a,done:false}

    /*
        0.4.yield 关键字
            只能在生成器函数内部使用,用在其他地方会报错(就和理解async await一样)
     */

    /*
        1.生成器对象作为可迭代对象
     */

    // 1.1简单使用
    function* generatorFn3() {
        yield 1;
        yield 2;
        yield 3;
    }

    for (const x of generatorFn3()) {
        console.log(x)  // 1 2 3
    }

    // 1.2 循环
    function* nTimes(n) {
        while (n--) {
            yield
        }
    }

    for (let _ of nTimes(3)) {
        console.log('foo') // foo foo foo
    }

    /*
        2.使用yield实现输入和输出
        fixme:开始有点不懂了
     */
    //  2.1 第一次调用 next()传入的值不会被使用，因为这一次调用是为了开始执行生成器函数
    function* generatorFn2_1(initial) {
        console.log(initial)
        console.log(yield)
        console.log(yield)
    }

    let g2_1 = generatorFn2_1('foo');
    g2_1.next('a') // foo
    g2_1.next('b') // b
    g2_1.next('c') // c

    // 2.2 yield关键字可以同时用于输入和输出
    function* generatorFn2_2() {
        return yield 'foo'
    }

    let g2_2 = generatorFn2_2();
    console.log(g2_2.next())    // {value:'foo',done:false}
    console.log(g2_2.next('bar'))    // {value:'bar',done:true}

    /*
        3.几个例子
     */
    // for
    function* nTimes1(n) {
        for (let i = 0; i < n; i++) {
            yield i
        }
    }

    for (let x of nTimes1(3)) {
        console.log(x)
    }

    // while
    function* nTimes2(n) {
        let i = 0;
        while (n--) {
            yield i++
        }
    }

    function* range(start, end) {
        while (end > start) {
            yield start++
        }
    }

    /*
        3.产生可迭代对象
     */
    // 3.1如下等同效果
    function* generatorFn3_1() {
        for (let x of [1, 2, 3]) {
            yield x;
        }
    }

    function* generatorFn3_2() {
        yield* [1, 2, 3]
    }

    // 3.2 可能有点不太理解
    // yield*的值是关联迭代器返回done:true时的value
    // 对于普通函数来说,返回undefined
    function* generatorFn3_3() {
        console.log('iter value:', yield* [1, 2, 3])
    }

    for (const x of generatorFn3_3()) {
        console.log('value:', x)
        // value: 1
        // value: 2
        // value: 3
        // iter value: undefined
    }

    /*
        4.使用yield* 实现递归算法
     */
    function* nTimes4_1(n) {
        if (n > 0) {
            yield* nTimes4_1(n - 1);
            yield n - 1
        }
    }

    for(const x of nTimes4_1(3)){
        console.log(x) // 0 1 2 
    }
    
    // 下面是一个图的例子,暂时不想深究
</script>
</body>
</html>